#-------------------------------------------------------------------------------
# Name:        1
# Purpose:
#
# Author:      z93446
#
# Created:     23/12/2015
# Copyright:   (c) z93446 2015
# Licence:     <your licence>
#-------------------------------------------------------------------------------

from Crc import ModbusCrc

class CModbus:
    
    FC_DCMP = 0x44
    FC_KEYPAD = 0x45
    FC_eCMP = 0x46
    
    def __init__(self, device):

        self.device = device
        self.slaveAddr = 1
        self.MEM_MSK = 0X4000
        self.BIWORD_MSK = 0X8000
        self.BIWORD_RAM_MSK = 0XC000
        #self.R_RES_COUNT_OFFSET = 4
        #self.R_RES_VALUE_OFFSET = 5
        self.R_RES_COUNT_OFFSET = 2
        self.R_RES_VALUE_OFFSET = 3
        self.FUNCODE_OFFSET = 1
        self.REG_ADDR_OFFSET = 2
        self.W_RES_VALUE_OFFSET = 4
        self.ERROR_FRAME = 0X18
        


    def SetDeviceAddr(self, addr):
        self.slaveAddr = addr


    def Read(self, Addr, count = 1, IsBiWords = False, IsMem = False):

        data = bytearray()
        data.append(self.slaveAddr)
        data.append(0x03)

        if (Addr & self.MEM_MSK) != 0:
            IsMem = True
        elif IsMem == True:
            Addr = Addr + self.MEM_MSK

        if (Addr & self.BIWORD_MSK) != 0:
            IsBiWords = True
        elif IsBiWords == True:
            Addr = Addr + self.BIWORD_MSK

        data.append((Addr >> 8) & 0xFF)
        data.append(Addr & 0xFF)

        data.append((count >> 8) & 0xFF)
        data.append(count & 0xFF)

        crc = ModbusCrc.Calc(data)
        data.append(crc & 0xFF)
        data.append((crc >> 8) & 0xFF)

        self.device.write(data)
        data = self.device.read(65535)

        crc = ModbusCrc.Calc(data[0:len(data) - 2])

        if ((crc >> 8) != data[len(data) - 1]) or ((crc & 0xFF) != data[len(data) - 2]) :
            return [self.ERROR_FRAME, [0]]

        if (data[1] & 0x80) == 0x80:
            return [data[2], [0]]


        #Addr = (ord(data[self.REG_ADDR_OFFSET]) << 8) + ord(data[self.REG_ADDR_OFFSET + 1])

        #IsBiWords = (Addr & self.BIWORD_MSK) != 0

        count = data[self.R_RES_COUNT_OFFSET] / 2;

        if IsBiWords == True:
            count = count / 2

        value = []
        offset = self.R_RES_VALUE_OFFSET

        while count > 0:

            if IsBiWords == True:
                value.append((data[offset] << 24) + (data[offset+1] << 16)+(data[offset+2] << 8)+data[offset+3])
                offset = offset + 4
            else:
                value.append((data[offset] << 8)+data[offset+1])
                offset = offset + 2

            count = count - 1

        return [0, value]


    def WriteSingle(self, Addr, value, IsBiWords = False, IsMem = False, Save = False):

        # Add slave address and function code
        data = bytearray()
        data.append(self.slaveAddr)
        if Save:
            data.append(0x41)
        else:
            data.append(0x06)

        # Append memory mask bit if required
        if IsMem == True:
            Addr = Addr + self.MEM_MSK

        # Append
        if IsBiWords == True:
            Addr = Addr + self.BIWORD_MSK
        elif (Addr & self.BIWORD_MSK) != 0:
            IsBiWords = True


        data.append((Addr >> 8) & 0xFF)
        data.append(Addr & 0xFF)

        if IsBiWords == False:

            data.append((value >> 8) & 0xFF)
            data.append(value & 0xFF)

        else:

            data.append((value >> 24) & 0xFF)
            data.append((value >> 16) & 0xFF)
            data.append((value >> 8) & 0xFF)
            data.append(value & 0xFF)

        crc = ModbusCrc.Calc(data)
        data.append(crc & 0xFF)
        data.append((crc >> 8) & 0xFF)


        self.device.write(data)
        data = self.device.read(65535)

        crc = ModbusCrc.Calc(data[0:len(data) - 2])

        if ((crc >> 8) != data[len(data) - 1]) or ((crc & 0xFF) != data[len(data) - 2]) :
            return [self.ERROR_FRAME, 0]

        if (data[self.FUNCODE_OFFSET] & 0x80) == 0x80:
            return [data[2], 0]

        Addr = (data[self.REG_ADDR_OFFSET] << 8) + data[self.REG_ADDR_OFFSET + 1]

        value = 0
        offset = self.W_RES_VALUE_OFFSET

        if (Addr & self.BIWORD_MSK) != 0:

            value = (data[offset] << 24) + (data[offset+1] << 16) + \
            (data[offset+2] << 8) + data[offset+3]

        else :

            value = (data[offset] << 8) + data[offset+1]


        return [0, value]



    def ParManAccess(self, request, quardByte):


        crc = ModbusCrc.Calc(request)
        request.append(crc & 0xFF)
        request.append((crc >> 8) & 0xFF)

        self.device.write(request)
        data = self.device.read(65535)

        crc = ModbusCrc.Calc(data[0:len(data) - 2])

        if ((crc >> 8) != data[len(data) - 1]) or ((crc & 0xFF) != data[len(data) - 2]) :
            return [self.ERROR_FRAME, 0]

        if (data[self.FUNCODE_OFFSET] & 0x80) == 0x80:
            return [data[2], 0]

        if quardByte:
            value = (data[4] << 24) + (data[5] << 16) + (data[6] << 8) + data[7]

        else:
            value = (data[4] << 8) + data[5]

        return [0, value]



    def GetParamRange(self, menu, param, rType):

        data = bytearray()

        data.append(self.slaveAddr)
        data.append(0x42)
        data.append(0x00)
        if rType.lower() == 'max':
            data.append(0x00)
        else:
            data.append(0x01)
        data.append(menu)
        data.append(param)

        return self.ParManAccess(data, True)


    def GetParamDefault(self, menu, param):

        data = bytearray()

        data.append(self.slaveAddr)
        data.append(0x42)
        data.append(0x00)
        data.append(0x08)
        data.append(menu)
        data.append(param)

        return self.ParManAccess(data, False)


    def GetParamFormat(self, menu, param):

        data = bytearray()

        data.append(self.slaveAddr)
        data.append(0x42)
        data.append(0x00)
        data.append(0x02)
        data.append(menu)
        data.append(param)

        return self.ParManAccess(data, False)



    def GetMaxParamInMenu(self, menu):

        data = bytearray()

        data.append(self.slaveAddr)
        data.append(0x42)
        data.append(0x00)
        data.append(0x03)
        data.append(menu)
        data.append(0x00)

        return self.ParManAccess(data, False)


    def GetNextMenu(self, menu):

        data = bytearray()

        data.append(self.slaveAddr)
        data.append(0x42)
        data.append(0x00)
        data.append(0x04)
        data.append(menu)
        data.append(0x00)

        error, value = self.ParManAccess(data, False)

        value = value >> 8

        return [error, value]

    def GetPrevMenu(self, menu):

        data = bytearray()

        data.append(self.slaveAddr)
        data.append(0x42)
        data.append(0x00)
        data.append(0x05)
        data.append(menu)
        data.append(0x00)

        error, value = self.ParManAccess(data, False)

        value = value >> 8

        return [error, value]


    def GetCurrentStatusFuncIndex(self):

        data = bytearray()

        data.append(self.slaveAddr)
        data.append(0x42)
        data.append(0x00)
        data.append(0x06)
        data.append(0x33)
        data.append(0x00)

        return self.ParManAccess(data, False)

    def GetNextStatusFuncIndex(self):
        data = bytearray()

        data.append(self.slaveAddr)
        data.append(0x42)
        data.append(0x00)
        data.append(0x07)
        data.append(0x33)
        data.append(0x00)

        return self.ParManAccess(data, False)



    def HandleDCMPRequest(self, request):
        self.ProtocolWrapper(CModbus.FC_DCMP, request)
        
    def HandleKeypadRequest(self, request):
        self.ProtocolWrapper(CModbus.FC_KEYPAD, request)


    def ProtocolWrapper(self, functionCode, request):
        
        data = bytearray()
        data.append(self.slaveAddr)
        data.append(functionCode)
        data = data + request
             
        crc = ModbusCrc.Calc(data)
        data.append(crc & 0xFF)
        data.append((crc >> 8) & 0xFF)   

        self.device.write(data)
        data = self.device.read(65535)   
            
        if data == None:
            return [self.ERROR_FRAME, 0]            
            
        crc = ModbusCrc.Calc(data[0:len(data) - 2])
        
        if ((crc >> 8) != data[len(data) - 1]) or ((crc & 0xFF) != data[len(data) - 2]) :
            return [self.ERROR_FRAME, 0]
        else:
            return [0, data[2:len(data)-2]]  


